/******************************************************************************* * Signavio Core Components * Copyright (C) 2012 Signavio GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package de.hpi.bpmn2_0.transformation; import java.security.InvalidKeyException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.oryxeditor.server.diagram.generic.GenericDiagram; import org.oryxeditor.server.diagram.generic.GenericEdge; import org.oryxeditor.server.diagram.generic.GenericShape; import de.hpi.bpmn2_0.annotations.CallingElement; import de.hpi.bpmn2_0.annotations.ContainerElement; import de.hpi.bpmn2_0.annotations.SSetExtension; import de.hpi.bpmn2_0.annotations.StencilId; import de.hpi.bpmn2_0.exceptions.BpmnConverterException; import de.hpi.bpmn2_0.factory.AbstractBpmnFactory; import de.hpi.bpmn2_0.factory.BPMNElement; import de.hpi.bpmn2_0.factory.configuration.Configuration; import de.hpi.bpmn2_0.factory.configuration.LinkedModel; import de.hpi.bpmn2_0.factory.node.IntermediateCatchEventFactory; import de.hpi.bpmn2_0.model.BaseElement; import de.hpi.bpmn2_0.model.Collaboration; import de.hpi.bpmn2_0.model.Definitions; import de.hpi.bpmn2_0.model.FlowElement; import de.hpi.bpmn2_0.model.FlowNode; import de.hpi.bpmn2_0.model.Process; import de.hpi.bpmn2_0.model.activity.Activity; import de.hpi.bpmn2_0.model.activity.CallActivity; import de.hpi.bpmn2_0.model.activity.SubProcess; import de.hpi.bpmn2_0.model.activity.Task; import de.hpi.bpmn2_0.model.artifacts.Artifact; import de.hpi.bpmn2_0.model.bpmndi.BPMNDiagram; import de.hpi.bpmn2_0.model.bpmndi.BPMNPlane; import de.hpi.bpmn2_0.model.bpmndi.BPMNShape; import de.hpi.bpmn2_0.model.callable.GlobalChoreographyTask; import de.hpi.bpmn2_0.model.choreography.CallChoreography; import de.hpi.bpmn2_0.model.choreography.Choreography; import de.hpi.bpmn2_0.model.choreography.ChoreographyActivity; import de.hpi.bpmn2_0.model.choreography.ChoreographyTask; import de.hpi.bpmn2_0.model.choreography.SubChoreography; import de.hpi.bpmn2_0.model.connector.Association; import de.hpi.bpmn2_0.model.connector.DataAssociation; import de.hpi.bpmn2_0.model.connector.DataInputAssociation; import de.hpi.bpmn2_0.model.connector.DataOutputAssociation; import de.hpi.bpmn2_0.model.connector.Edge; import de.hpi.bpmn2_0.model.connector.MessageFlow; import de.hpi.bpmn2_0.model.connector.SequenceFlow; import de.hpi.bpmn2_0.model.conversation.ConversationElement; import de.hpi.bpmn2_0.model.conversation.ConversationLink; import de.hpi.bpmn2_0.model.conversation.ConversationNode; import de.hpi.bpmn2_0.model.data_object.AbstractDataObject; import de.hpi.bpmn2_0.model.data_object.DataInput; import de.hpi.bpmn2_0.model.data_object.DataOutput; import de.hpi.bpmn2_0.model.data_object.DataStoreReference; import de.hpi.bpmn2_0.model.data_object.InputOutputSpecification; import de.hpi.bpmn2_0.model.data_object.InputSet; import de.hpi.bpmn2_0.model.data_object.Message; import de.hpi.bpmn2_0.model.data_object.OutputSet; import de.hpi.bpmn2_0.model.event.BoundaryEvent; import de.hpi.bpmn2_0.model.event.CompensateEventDefinition; import de.hpi.bpmn2_0.model.event.Event; import de.hpi.bpmn2_0.model.event.SignalEventDefinition; import de.hpi.bpmn2_0.model.extension.ExtensionElements; import de.hpi.bpmn2_0.model.extension.signavio.SignavioMessageName; import de.hpi.bpmn2_0.model.gateway.Gateway; import de.hpi.bpmn2_0.model.gateway.GatewayWithDefaultFlow; import de.hpi.bpmn2_0.model.misc.ProcessType; import de.hpi.bpmn2_0.model.participant.Lane; import de.hpi.bpmn2_0.model.participant.LaneSet; import de.hpi.bpmn2_0.model.participant.Participant; import de.hpi.bpmn2_0.util.DiagramHelper; import de.hpi.bpmn2_0.util.SignavioIDChecker; import de.hpi.diagram.SignavioUUID; /** * Converter class for Diagram to BPMN 2.0 transformation. * * @author Philipp Giese * @author Sven Wagner-Boysen * */ public class Diagram2BpmnConverter { /* Hash map of factories for BPMN 2.0 element to enable lazy initialization */ private HashMap<String, AbstractBpmnFactory> factories; private HashMap<String, BPMNElement> bpmnElements; private GenericDiagram<?,?> diagram; private List<BPMNElement> diagramChilds; private List<Process> processes; private static Constants constants; /* Processes just used to identify all processes in the diagram (used i.e. * for CallActivities). They are removed afterwards, to avoid duplicates. */ private List<Process> tmpProcesses; private Definitions definitions; private Configuration configuration; private String editorVersion; private Collaboration collaboration; private List<Choreography> choreography; private List<Class<? extends AbstractBpmnFactory>> factoryClasses; /* Define edge ids */ private final static String[] edgeIdsArray = { "SequenceFlow", "Association_Undirected", "Association_Unidirectional", "Association_Bidirectional", "MessageFlow", "ConversationLink" }; public final static HashSet<String> edgeIds = new HashSet<String>(Arrays .asList(edgeIdsArray)); /* Define data related objects ids */ private final static String[] dataObjectIdsArray = { "DataObject", "DataStore", "Message", "ITSystem" }; public final static HashSet<String> dataObjectIds = new HashSet<String>( Arrays.asList(dataObjectIdsArray)); public Diagram2BpmnConverter(GenericDiagram diagram, List<Class<? extends AbstractBpmnFactory>> factoryClasses) { this.factories = new HashMap<String, AbstractBpmnFactory>(); this.bpmnElements = new HashMap<String, BPMNElement>(); this.definitions = new Definitions(); this.definitions.setId(SignavioUUID.generate()); this.diagram = diagram; this.factoryClasses = factoryClasses; this.configuration = new Configuration(); } /** * Also sets the configuration on reformation the element IDs. * * @param diagram * @param factoryClasses */ public Diagram2BpmnConverter(GenericDiagram diagram, List<Class<? extends AbstractBpmnFactory>> factoryClasses, boolean checkOnSignavioIDs) { this(diagram, factoryClasses); this.configuration.ensureSignavioStyle = checkOnSignavioIDs; } /** * Constructor receiving an additional configuration map. This map can * contain linked diagram or the current editor version. * * @param diagram2 * @param factoryClasses2 * @param configuration */ public Diagram2BpmnConverter(GenericDiagram diagram, List<Class<? extends AbstractBpmnFactory>> factoryClasses, Map<String, Object> configuration) { this(diagram, factoryClasses); if (configuration == null) return; /* Process configuration */ this.configuration = new Configuration(configuration); /* Editor version */ setEditorVersion(this.configuration); } public Diagram2BpmnConverter(Map<String, LinkedModel> linkedModels, GenericDiagram diagram, List<Class<? extends AbstractBpmnFactory>> factoryClasses) { this(diagram, factoryClasses); this.configuration.getLinkedModels().putAll(linkedModels); } private void setEditorVersion(Configuration configuration) { if (configuration == null) { return; } this.editorVersion = configuration.getEditorVersion(); } /** * Retrieves the stencil id related hashed factory. * * @param stencilId * The stencil id * @return The related factory * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ private AbstractBpmnFactory getFactoryForStencilId(String stencilId) throws ClassNotFoundException, InstantiationException, IllegalAccessException { /* Create a new factory instance if necessary */ if (!factories.containsKey(stencilId)) { this.factories.put(stencilId, createFactoryForStencilId(stencilId)); } return this.factories.get(stencilId); } /** * Creates a new factory instance for a stencil id. * * @param stencilId * The stencil id * @return The created factory * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException * */ private AbstractBpmnFactory createFactoryForStencilId(String stencilId) throws ClassNotFoundException, InstantiationException, IllegalAccessException { /* Find factory for stencil id */ Class<? extends AbstractBpmnFactory> factory = null; for (Class<? extends AbstractBpmnFactory> factoryClass : factoryClasses) { StencilId stencilIdA = (StencilId) factoryClass .getAnnotation(StencilId.class); if (stencilIdA == null) continue; /* Check if appropriate stencil id is contained */ List<String> stencilIds = Arrays.asList(stencilIdA.value()); if (stencilIds.contains(stencilId)) { if (factory == null) factory = factoryClass; else { /* * Prefer the general factory class if the necessary stencil * set extension of the specialized factory class is not * loaded in the diagram. */ SSetExtension oldSSetExtension = factory .getAnnotation(SSetExtension.class); if (oldSSetExtension != null) { if (!this.diagram.getSsextensions().containsAll( Arrays.asList(oldSSetExtension.value()))) { factory = factoryClass; continue; } } /* * Check if there is a specialized factory for an loaded * extension */ SSetExtension ssetExtension = factoryClass .getAnnotation(SSetExtension.class); if (ssetExtension == null) continue; if (this.diagram.getSsextensions().containsAll( Arrays.asList(ssetExtension.value()))) factory = factoryClass; } } } if (factory != null) return factory.newInstance(); throw new ClassNotFoundException("Factory for stencil id: '" + stencilId + "' not found!"); } /** * Secures uniqueness of an BPMN Element. * * @param el * @throws InvalidKeyException */ private void addBpmnElement(BPMNElement el) throws InvalidKeyException { if (this.bpmnElements.containsKey(el.getId())) { throw new InvalidKeyException( "Key already exists for BPMN element!"); } this.bpmnElements.put(el.getId(), el); } /** * Creates the BPMN 2.0 elements for the parent's child shapes recursively. * * @param childShapes * The list of parent's child shapes * @param parent * The parent {@link BPMNElement} * * @throws ClassNotFoundException * @throws InstantiationException * @throws IllegalAccessException * @throws BpmnConverterException * @throws InvalidKeyException */ private BPMNElement createBpmnElementsRecursively(GenericShape<?,?> shape) throws ClassNotFoundException, InstantiationException, IllegalAccessException, BpmnConverterException, InvalidKeyException { /* Build up the Elements of the current shape childs */ ArrayList<BPMNElement> childElements = new ArrayList<BPMNElement>(); /* Create BPMN elements from shapes */ for (GenericShape childShape : shape.getChildShapesReadOnly()) { BPMNElement childEl = this.createBpmnElementsRecursively(childShape); if(childEl != null) { childElements.add(childEl); } } if (shape.equals(this.diagram)) { this.diagramChilds = childElements; return null; } /* Get the appropriate factory and create the element */ AbstractBpmnFactory factory = this.getFactoryForStencilId(shape .getStencilId()); BPMNElement bpmnElement = null; if (this.configuration == null) { bpmnElement = factory.createBpmnElement(shape, new BPMNElement( null, null, null)); } else { bpmnElement = factory.createBpmnElement(shape, this.configuration); } if(bpmnElement != null) { /* Add element to flat list of all elements of the diagram */ this.addBpmnElement(bpmnElement); /* Add childs to current BPMN element */ for (BPMNElement child : childElements) { bpmnElement.addChild(child); } } return bpmnElement; } /** * Finds catching intermediate event that are attached to an activities * boundary. */ private void detectBoundaryEvents() { for (GenericShape<?,?> shape : this.diagram.getAllShapesReadOnly()) { if (edgeIds.contains(shape.getStencilId())) { continue; } for (GenericShape outShape : shape.getOutgoingsReadOnly()) { if (edgeIds.contains(outShape.getStencilId())) continue; IntermediateCatchEventFactory.changeToBoundaryEvent( this.bpmnElements.get(shape.getResourceId()), this.bpmnElements.get(outShape.getResourceId())); } } } /** * Retrieves the edges and updates the source and target references. */ private void detectConnectors() { for (GenericEdge<?,?> edge : DiagramHelper.getAllEdges(this.diagram)) { //TODO do we still need this? if (!edgeIds.contains(edge.getStencilId())) { continue; } /* Retrieve connector element */ BPMNElement bpmnConnector = this.bpmnElements.get(edge .getResourceId()); BPMNElement source = null; /* * Find source of connector. It is assumed that the first none edge * element is the source element. */ for (GenericShape incomingShape : edge.getIncomingsReadOnly()) { if (edgeIds.contains(incomingShape.getStencilId())) { ((Edge) bpmnConnector.getNode()).getIncoming().add( (Edge) this.bpmnElements.get( incomingShape.getResourceId()).getNode()); /* * Avoids that a sequence/message flow or conversation link * has an edge element as source */ if (bpmnConnector.getNode() instanceof SequenceFlow || bpmnConnector.getNode() instanceof MessageFlow || bpmnConnector.getNode() instanceof ConversationLink) { continue; } } source = this.bpmnElements.get(incomingShape.getResourceId()); break; } /* Update outgoing references */ for (GenericShape outgoingShape : edge.getOutgoingsReadOnly()) { if (!edgeIds.contains(outgoingShape.getStencilId())) continue; ((Edge) bpmnConnector.getNode()).getOutgoing().add( (Edge) this.bpmnElements.get( outgoingShape.getResourceId()).getNode()); } BPMNElement target = (edge.getTarget() != null) ? this.bpmnElements .get(edge.getTarget().getResourceId()) : null; /* Update source references */ if (source != null) { Edge edgeElement = (Edge) bpmnConnector.getNode(); FlowElement sourceNode = (FlowElement) source.getNode(); sourceNode.getOutgoing().add((Edge) bpmnConnector.getNode()); edgeElement.setSourceRef(sourceNode); } /* Update target references */ if (target != null) { Edge edgeElement = (Edge) bpmnConnector.getNode(); FlowElement targetNode = (FlowElement) target.getNode(); targetNode.getIncoming().add((Edge) bpmnConnector.getNode()); edgeElement.setTargetRef(targetNode); } } } /** * An undirected association that connects a sequence flow and a data object * is split up into a data input and output association. */ private void updateUndirectedDataAssociationsRefs() { for (GenericShape shape : this.diagram.getAllShapesReadOnly()) { if (shape.getStencilId() == null || !shape.getStencilId().equalsIgnoreCase("sequenceflow")) continue; /* Retrieve sequence flow connector element */ BPMNElement seqFlowEle = this.bpmnElements.get(shape .getResourceId()); if (seqFlowEle.getNode() instanceof SequenceFlow) ((SequenceFlow) seqFlowEle.getNode()) .processUndirectedDataAssociations(); } } /** * A {@link DataAssociation} is a child element of an {@link Activity}. This * method updates the references between activities and their data * associations. */ private void updateDataAssociationsRefs() { /* Define edge ids */ String[] associationIdsArray = { /* "Association_Undirected", */ "Association_Unidirectional", "Association_Bidirectional" }; HashSet<String> associationIds = new HashSet<String>(Arrays .asList(associationIdsArray)); for (GenericShape shape : this.diagram.getAllShapesReadOnly()) { if (!associationIds.contains(shape.getStencilId())) { continue; } /* Retrieve connector element */ BPMNElement bpmnConnector = this.bpmnElements.get(shape .getResourceId()); /* Get related activity */ Edge dataAssociation = (Edge) bpmnConnector.getNode(); Activity relatedActivity = null; if (dataAssociation instanceof DataInputAssociation) { relatedActivity = (dataAssociation.getTargetRef() instanceof Activity ? (Activity) dataAssociation .getTargetRef() : null); if (relatedActivity != null) { relatedActivity.getDataInputAssociation().add( (DataInputAssociation) dataAssociation); } else { bpmnConnector.setNode(new Association((DataInputAssociation)dataAssociation)); } } else if (dataAssociation instanceof DataOutputAssociation) { relatedActivity = (dataAssociation.getSourceRef() instanceof Activity ? (Activity) dataAssociation .getSourceRef() : null); if (relatedActivity != null) { relatedActivity.getDataOutputAssociation().add( (DataOutputAssociation) dataAssociation); } else { bpmnConnector.setNode(new Association((DataOutputAssociation)dataAssociation)); } } } /* Update undirected data associations references */ this.updateUndirectedDataAssociationsRefs(); } /** * Identifies the default sequence flows after all sequence flows are set * correctly. */ private void setDefaultSequenceFlowOfExclusiveGateway() { for (BPMNElement element : this.bpmnElements.values()) { BaseElement base = element.getNode(); if (base instanceof GatewayWithDefaultFlow) { ((GatewayWithDefaultFlow) base).findDefaultSequenceFlow(); } } } /** * Method to handle sub processes * * @param subProcess */ private void handleSubProcess(SubProcess subProcess) { List<BPMNElement> childs = this.getChildElements(this.bpmnElements .get(subProcess.getId())); for (BPMNElement ele : childs) { // process.getFlowElement().add((FlowElement) ele.getNode()); // subProcess.getFlowElement().add((FlowElement) ele.getNode()); if (ele.getNode() instanceof SubProcess) { this.handleSubProcess((SubProcess) ele.getNode()); } else if(ele.getNode() instanceof CallActivity) { this.handleCallActivities((CallActivity) ele.getNode()); } } } /** * In case the {@link CallActivity} calls an expanded subprocess, a new * process is created that includes all referenced {@link FlowElement} by * the {@link CallActivity} * * @param callAct */ private void handleCallActivities(CallActivity callAct) { Process p = new Process(); p.setId(""); for(FlowElement fe : callAct._getFlowElementsOfTheGlobalProcess()) { p.getFlowElement().add(fe); if(fe instanceof CallActivity) { handleCallActivities((CallActivity) fe); } else if(fe instanceof SubProcess) { handleSubProcess((SubProcess) fe); } } // Apply id for BPMN 2.0 XML round trip if applicable if(!p.hasId() && callAct.hasValidRoundTripProcessId()) { p.setId(callAct.getProcessid()); } else if (!p.hasId()) { p.setId(SignavioUUID.generate()); } if(p.getFlowElement().size() > 0) { callAct.setCalledElement(p); this.processes.add(p); this.tmpProcesses.add(p); } } /** * Identifies {@link Artifact} elements and puts them into the appropriate * {@link Process} element. */ private void handleArtifacts() { for (Artifact artifact : this.getAllArtifacts()) { /* * Prefer the process by connecting object over process by pool * containment. Use case: task in Pool1 text annotation in Pool2 */ Process containmentProcess = artifact.getProcess(); SubProcess subProcess = artifact.getSubProcess(); SubChoreography subChoreography = artifact.getSubChoreography(); artifact.findRelatedProcess(); /* * If no process was found, check it the artifact is part of a * conversation. */ if (artifact.getProcess() == null && containmentProcess == null && subProcess == null && subChoreography == null && artifact.isConverstionRelated()) { getCollaboration().getArtifact().add(artifact); continue; } /* * If a new process was assigned, delete the artifact as a child * element from the old process */ if (containmentProcess != null && artifact.getProcess() != null && !containmentProcess.getId().equals( artifact.getProcess().getId())) { containmentProcess.removeChild(artifact); } /* Remove from subprocess if e.g. a Textannotation was missplaced */ if (subProcess != null && artifact.getProcess() != null) { subProcess.removeChild(artifact); } /* * If no related process was found, add assign to the default * process. */ if (subProcess == null && artifact.getProcess() == null && this.processes.size() > 0) { artifact.setProcess(this.processes .get(this.processes.size() - 1)); this.processes.get(this.processes.size() - 1) .addChild(artifact); } else if (subProcess == null && artifact.getProcess() == null) { Process process = new Process(); this.processes.add(process); process.setId(SignavioUUID.generate()); process.addChild(artifact); artifact.setProcess(process); } } } /** * Assigns the DataObjectes to the appropriate {@link Process}. */ private void handleDataObjects() { ArrayList<AbstractDataObject> dataObjects = new ArrayList<AbstractDataObject>(); this.getAllDataObjects(this.diagramChilds, dataObjects); for (AbstractDataObject dataObject : dataObjects) { if (dataObject.getProcess() != null) continue; dataObject.findRelatedProcess(); /* Add a DataStore as a global element */ if (dataObject instanceof DataStoreReference && ((DataStoreReference) dataObject).getDataStoreRef() != null) { this.definitions.getRootElement().add( ((DataStoreReference) dataObject).getDataStoreRef()); } /* * If no related process was found, add assign to the default * process. */ if (dataObject.getProcess() == null && this.processes.size() > 0) { dataObject.setProcess(this.processes .get(this.processes.size() - 1)); this.processes.get(this.processes.size() - 1).addChild( dataObject); } else if (dataObject.getProcess() == null) { Process process = new Process(); this.processes.add(process); process.setId(SignavioUUID.generate()); process.addChild(dataObject); dataObject.setProcess(process); } } } /** * Retrieves all data related elements. * * @param elements * The list of {@link BPMNElement}. * * @param dataObjects * The resulting list of {@link AbstractDataObject} */ private void getAllDataObjects(List<BPMNElement> elements, List<AbstractDataObject> dataObjects) { for (BPMNElement element : elements) { if (element.getNode() instanceof Lane || element.getNode() instanceof SubProcess) { getAllDataObjects(this.getChildElements(element), dataObjects); continue; } if (element.getNode() instanceof AbstractDataObject) { dataObjects.add((AbstractDataObject) element.getNode()); } } } /** * * @return All {@link Artifact} contained in the diagram. */ private List<Artifact> getAllArtifacts() { List<Artifact> artifacts = new ArrayList<Artifact>(); for (BPMNElement element : this.bpmnElements.values()) { if (element.getNode() instanceof Artifact) { artifacts.add((Artifact) element.getNode()); } } return artifacts; } /** * @return All {@link Task} contained in the diagram. */ private List<Task> getAllTasks() { ArrayList<Task> activities = new ArrayList<Task>(); for (BPMNElement element : this.bpmnElements.values()) { if (element.getNode() instanceof Task) activities.add((Task) element.getNode()); } return activities; } /** * Identifies sets of nodes, connected through SequenceFlows. */ private void identifyProcesses() { this.processes = new ArrayList<Process>(); this.tmpProcesses = new ArrayList<Process>(); List<FlowNode> allNodes = new ArrayList<FlowNode>(); this.getAllNodesRecursively(this.diagramChilds, allNodes); // handle subprocesses => trivial for (FlowNode flowNode : allNodes) { if (flowNode instanceof SubProcess) { handleSubProcess((SubProcess) flowNode); } else if(flowNode instanceof CallActivity) { handleCallActivities((CallActivity) flowNode); } } /* Handle pools, current solution: only one process per pool */ for (BPMNElement element : this.diagramChilds) { if (element.getNode() instanceof Participant && ((Participant) element.getNode()).getLaneSet() != null) { Participant participant = (Participant) element.getNode(); LaneSet laneSet = participant.getLaneSet(); Process process = new Process(); process.setId(""); // Retrieve process id from pool element to keep the process id // in a BPMN 2.0 XML round trip scenario. if(participant.hasValidRoundTripProcessId()) { process.setId(participant.getProcessid()); } else { process.setId(SignavioUUID.generate()); } /* Process attributes derived from lane set */ /* isCloased */ if (participant._isClosed != null && participant._isClosed.equalsIgnoreCase("true")) process.setIsClosed(true); else process.setIsClosed(false); /* Process Type */ if (participant._processType != null) { process.setProcessType(ProcessType .fromValue(participant._processType)); } /* isExecutable */ if (participant._isExecutable != null && participant._isExecutable.equalsIgnoreCase("true")) process.setExecutable(true); else process.setExecutable(false); process.getLaneSet().add(laneSet); participant.setProcessRef(process); /* Copy name attribute of participant */ process.setName(participant.getName()); process.getFlowElement().addAll(laneSet.getChildFlowElements()); this.processes.add(process); } } /* Identify components within allNodes */ while (allNodes.size() > 0) { Process currentProcess = new Process(); currentProcess.setId(""); if(this.definitions.getName() != null && this.definitions.getName().length() > 0) { currentProcess.setName(this.definitions.getName()); } this.processes.add(currentProcess); addNode(currentProcess, this.getBpmnElementForNode(allNodes.get(0)), allNodes); // Generate Process Id if necessary if(!currentProcess.hasId()) { currentProcess.setId(SignavioUUID.generate()); } } this.addSequenceFlowsToProcess(); /* Set processRefs */ for (Process p : this.processes) { for (FlowElement el : p.getFlowElement()) { el.setProcess(p); } } /* Remove temporary processes from the list */ this.processes.removeAll(this.tmpProcesses); // this.tmpProcesses = null; } /** * Adds {@link Edge} to the related process. */ private void addSequenceFlowsToProcess() { for (BPMNElement element : this.diagramChilds) { if (!(element.getNode() instanceof SequenceFlow)) continue; Edge edge = (Edge) element.getNode(); List<FlowElement> flowElements = findProcessFlowElementListForEdge(edge); if (flowElements != null) { flowElements.add(edge); } } } /** * Finds the process for an {@link Edge} * * @param edge */ private List<FlowElement> findProcessFlowElementListForEdge(Edge edge) { /* Find process for edge */ for (Process process : this.processes) { List<FlowElement> flowElements; flowElements = process.getFlowElement(); List<Artifact> artifacts = process.getArtifact(); if (flowElements.contains(edge.getSourceRef()) || flowElements.contains(edge.getTargetRef()) || artifacts.contains(edge.getSourceRef()) || artifacts.contains(edge.getTargetRef())) { edge.setProcess(process); return flowElements; } /* Look up in subprocesses */ for (SubProcess subProcess : process.getSubprocessList()) { flowElements = subProcess.getFlowElement(); if (flowElements.contains(edge.getSourceRef()) || flowElements.contains(edge.getTargetRef())) { edge.setSubProcess(subProcess); return flowElements; } } /* Look up in subchoreographies */ for (SubChoreography subChoreography : process .getSubChoreographyList()) { flowElements = subChoreography.getFlowElement(); if (flowElements.contains(edge.getSourceRef()) || flowElements.contains(edge.getTargetRef())) { edge.setSubChoreography(subChoreography); return flowElements; } } } return null; } /** * Assigns {@link Association} to the appropriate process element. */ private void addAssociationsToProcess() { for (BPMNElement element : this.diagramChilds) { if (!(element.getNode() instanceof Association)) continue; Edge edge = (Edge) element.getNode(); List<FlowElement> flowElements = findProcessFlowElementListForEdge(edge); if (flowElements != null) { ((Association) edge)._containedInProcess = true; flowElements.add(edge); } } } /** * Assigns {@link Association} to the collaboration element in case of a * conversation. */ private void addAssociationsToConversation() { for (BPMNElement element : this.diagramChilds) { if (!(element.getNode() instanceof Association)) continue; Association edge = (Association) element.getNode(); if (edge._containedInProcess) { continue; } /* Check source */ if (edge.getSourceRef() != null && edge.getSourceRef() instanceof ConversationElement && !(edge.getSourceRef() instanceof Participant && ((Participant) edge .getSourceRef())._isChoreographyParticipant) && (edge.getSourceRef().getProcess() == null || (edge .getSourceRef().getProcess() != null && !edge .getSourceRef().getProcess() .isChoreographyProcess()))) { this.getCollaboration().getAssociation().add(edge); continue; } /* Check target */ if (edge.getTargetRef() != null && edge.getTargetRef() instanceof ConversationElement) { this.getCollaboration().getAssociation().add(edge); continue; } } } /** * Helper method to get the {@link BPMNElement} for the given * {@link FlowNode} from the list of BPMN elements. * * @param node * The concerning {@link FlowNode} * @return The related {@link BPMNElement} */ private BPMNElement getBpmnElementForNode(FlowNode node) { return this.bpmnElements.get(node.getId()); } /** * Adds the node to the connected set of nodes. * * @param process * @param element * @param allNodes */ private void addNode(Process process, BPMNElement element, List<FlowNode> allNodes) { if (!(element.getNode() instanceof FlowNode) || !allNodes.contains(element.getNode())) { return; } FlowNode node = (FlowNode) element.getNode(); // Set id of process if there is one applied for round-tripping scenarios if(!process.hasId() && node.hasValidRoundTripProcessId()) { process.setId(node.getProcessid()); } allNodes.remove(node); node.setProcess(process); process.addChild(node); /* Handle sequence flows */ /* Attention: navigate into both directions! */ for (SequenceFlow seqFlow : node.getIncomingSequenceFlows()) { if (seqFlow.sourceAndTargetContainedInSamePool()) { addNode(process, this.getBpmnElementForNode((FlowNode) seqFlow .getSourceRef()), allNodes); } } for (SequenceFlow seqFlow : node.getOutgoingSequenceFlows()) { if (seqFlow.sourceAndTargetContainedInSamePool()) { addNode(process, this.getBpmnElementForNode((FlowNode) seqFlow .getTargetRef()), allNodes); } } /* Handle compensation flow */ /* Attention: navigate into both directions! */ for (Association compFlow : node.getIncomingCompensationFlows()) { if (compFlow.sourceAndTargetContainedInSamePool()) { addNode(process, this.getBpmnElementForNode((FlowNode) compFlow .getSourceRef()), allNodes); } } for (Association compFlow : node.getOutgoingCompensationFlows()) { if (compFlow.sourceAndTargetContainedInSamePool()) { addNode(process, this.getBpmnElementForNode((FlowNode) compFlow .getTargetRef()), allNodes); } } /* Handle boundary events */ /* Attention: navigate into both directions! */ if (node instanceof BoundaryEvent) { if (((BoundaryEvent) node).getAttachedToRef() != null) { addNode(process, this .getBpmnElementForNode(((BoundaryEvent) node) .getAttachedToRef()), allNodes); } } else if (node instanceof Activity) { for (BoundaryEvent event : ((Activity) node).getBoundaryEventRefs()) { addNode(process, this.getBpmnElementForNode(event), allNodes); } } } /** * Retrieves all nodes included into the diagram and stop recursion at * subprocesses. * * @param elements * The child elements of a parent BPMN element * @param allNodes * The list to store every element */ private void getAllNodesRecursively(List<BPMNElement> elements, List<FlowNode> allNodes) { for (BPMNElement element : elements) { if (element.getNode() instanceof Lane) { getAllNodesRecursively(this.getChildElements(element), allNodes); continue; } if (!(element.getNode() instanceof FlowNode)) { continue; } FlowNode node = (FlowNode) element.getNode(); if (node instanceof Activity || node instanceof Event || node instanceof Gateway) { allNodes.add(node); } } } /** * Retrieve the child elements of a BPMN element from within all BPMN * elements in the diagram. * * @param element * The parent BPMN Element * @return */ private List<BPMNElement> getChildElements(BPMNElement element) { List<BPMNElement> childElements = new ArrayList<BPMNElement>(); for (GenericShape<?,?> shape : this.diagram.getAllShapesReadOnly()) { if (!shape.getResourceId().equals(element.getId())) { continue; } for (GenericShape child : shape.getChildShapesReadOnly()) { childElements.add(this.bpmnElements.get(child.getResourceId())); } } return childElements; } /** * Inserts the shapes of nodes as children of the {@link BPMNPlane}. * * @param subProcess */ private void insertSubprocessShapes(SubProcess subProcess) { for (FlowElement flowEle : subProcess.getFlowElement()) { if (flowEle instanceof FlowNode && this.bpmnElements.get(flowEle.getId()) != null) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(flowEle.getId()).getShape()); } /* Subprocess elements */ if (flowEle instanceof SubProcess) { insertSubprocessShapes((SubProcess) flowEle); } } for (Artifact a : subProcess.getArtifact()) { if(this.bpmnElements.get(a.getId()) != null) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(a.getId()).getShape()); } } } /** * Places the shapes of the elements referenced by the process at diagram's * shape list. * * @param process */ private boolean insertProcessShapes(Process process) { if (process.isChoreographyProcess()) return false; /* First insert LaneSets */ for (Lane lane : process.getAllLanes()) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(lane.getId()).getShape()); } /* Second process elements like tasks */ for (FlowElement flowEle : process.getFlowElement()) { if (!(flowEle instanceof Edge)) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(flowEle.getId()).getShape()); } /* Subprocess elements */ if (flowEle instanceof SubProcess) { insertSubprocessShapes((SubProcess) flowEle); } } /* Insert Artifacts */ for (Artifact a : process.getArtifact()) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(a.getId()).getShape()); } /* Insert Data Objects form IOSpecifications */ if(process.getIoSpecification() != null) { for(DataInput dataInput : process.getIoSpecification().getDataInput()) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(dataInput.getId()).getShape()); } for(DataOutput dataOutput : process.getIoSpecification().getDataOutput()) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(dataOutput.getId()).getShape()); } } return true; } /** * Creates a process diagram for each identified process. */ private void insertProcessesIntoDefinitions() { for (Process process : this.processes) { // if (process.isChoreographyProcess()) // continue; // // /* First insert LaneSets */ // for (Lane lane : process.getAllLanes()) { // this.definitions.getFirstPlane().getDiagramElement().add( // this.bpmnElements.get(lane.getId()).getShape()); // } // // /* Second process elements like tasks */ // // for (FlowElement flowEle : process.getFlowElement()) { // if (!(flowEle instanceof Edge)) { // this.definitions.getFirstPlane().getDiagramElement().add( // this.bpmnElements.get(flowEle.getId()).getShape()); // } // // /* Subprocess elements */ // if (flowEle instanceof SubProcess) { // insertSubprocessShapes((SubProcess) flowEle); // } // } // // /* Insert Artifacts */ // for (Artifact a : process.getArtifact()) { // this.definitions.getFirstPlane().getDiagramElement().add( // this.bpmnElements.get(a.getId()).getShape()); // } // // /* Insert Data Objects form IOSpecifications */ // if(process.getIoSpecification() != null) { // for(DataInput dataInput : process.getIoSpecification().getDataInput()) { // this.definitions.getFirstPlane().getDiagramElement().add( // this.bpmnElements.get(dataInput.getId()).getShape()); // } // // for(DataOutput dataOutput : process.getIoSpecification().getDataOutput()) { // this.definitions.getFirstPlane().getDiagramElement().add( // this.bpmnElements.get(dataOutput.getId()).getShape()); // } // } if(!this.insertProcessShapes(process)) { continue; } /* Insert process into document */ this.definitions.getRootElement().add(process); /* * Set diagram shape reference for the process. This reference will * be overwritten, if a collaboration is contained. */ if(!(this.definitions.getFirstPlane().getBpmnElement() instanceof Collaboration) && !(this.definitions.getFirstPlane().getBpmnElement() instanceof Choreography)) { this.definitions.getFirstPlane().setBpmnElement(process); } /* * Insert just the shapes of the temporary processes, because * their process elements are already connected to and so inserted * in the definitions */ for(Process p : this.tmpProcesses) { this.insertProcessShapes(p); } } } /** * Set the reference to the activity related to the compensation. */ private void setCompensationEventActivityRef() { for (BPMNElement element : this.bpmnElements.values()) { /* * Processing only necessary for events with compensation event * definition */ if (!(element.getNode() instanceof Event)) return; if (((Event) element.getNode()) .getEventDefinitionOfType(CompensateEventDefinition.class) == null) if (element.getNode() instanceof BoundaryEvent && ((BoundaryEvent) element.getNode()) .getEventDefinitionOfType(CompensateEventDefinition.class) != null) { BoundaryEvent bEvent = (BoundaryEvent) element.getNode(); ((CompensateEventDefinition) bEvent .getEventDefinitionOfType(CompensateEventDefinition.class)) .setActivityRef(bEvent.getAttachedToRef()); } } } /** * Set the initiating participant of a choreography activity. */ private void setInitiatingParticipant() { for (BPMNElement element : this.bpmnElements.values()) { if (element.getNode() instanceof ChoreographyActivity) { ChoreographyActivity activity = (ChoreographyActivity) element .getNode(); for (Participant partici : activity.getParticipantRef()) { if (partici.isInitiating()) { activity.setInitiatingParticipantRef(partici); break; } } } } } /** * Inserts {@link Participant} to the {@link Collaboration} element and * assigns the collaboration to the appropriate {@link BPMNPlane} element. */ private void insertCollaborationElements() { for (BPMNElement bpmnElement : this.bpmnElements.values()) { /* Insert participants */ if (bpmnElement.getNode() instanceof Participant && !((Participant) bpmnElement.getNode())._isChoreographyParticipant) { /* Insert semantics element */ getCollaboration().getParticipant().add( (Participant) bpmnElement.getNode()); /* Insert diagram element */ this.definitions.getFirstPlane().getDiagramElement().add( bpmnElement.getShape()); } /* Conversation elements */ else if (this.insertConversationElements(bpmnElement)) { } /* Message Flows */ else if (bpmnElement.getNode() instanceof MessageFlow) { getCollaboration().getMessageFlow().add( (MessageFlow) bpmnElement.getNode()); } } /* Set collaboration references */ if (this.collaboration != null) { /* Insert shapes of artifacts */ for (Artifact a : getCollaboration().getArtifact()) { this.definitions.getFirstPlane().getDiagramElement().add( bpmnElements.get(a.getId()).getShape()); } /* Insert association shapes */ for (Association a : getCollaboration().getAssociation()) { this.definitions.getFirstPlane().getDiagramElement().add( bpmnElements.get(a.getId()).getShape()); } this.definitions.getRootElement().add(this.collaboration); this.definitions.getFirstPlane().setBpmnElement(this.collaboration); } } /** * Checks if the element is an conversation elements and appends it to the * appropriate list. */ private boolean insertConversationElements(BPMNElement element) { // this.identifyConversation(); /* Conversation nodes */ if (element.getNode() instanceof ConversationNode) { /* Semantics element */ getCollaboration().getConversationNode().add( (ConversationNode) element.getNode()); /* Insert diagram element */ this.definitions.getFirstPlane().getDiagramElement().add( element.getShape()); return true; } /* Conversation link */ else if (element.getNode() instanceof ConversationLink) { getCollaboration().getConversationLink().add( (ConversationLink) element.getNode()); return true; } return false; } /** * Sets the message is visible flag to the first participant * * @param association */ public void handleMessageAssociationOnChoreographyActivity( Association association) { /* Retrieve choreography acitivity */ ChoreographyActivity choreoActivity = null; Message message = null; if (association.getSourceRef() instanceof ChoreographyActivity && association.getTargetRef() instanceof Message) { choreoActivity = (ChoreographyActivity) association.getSourceRef(); message = (Message) association.getTargetRef(); } else if (association.getTargetRef() instanceof ChoreographyActivity && association.getSourceRef() instanceof Message) { choreoActivity = (ChoreographyActivity) association.getTargetRef(); message = (Message) association.getSourceRef(); } if (choreoActivity != null && message != null && choreoActivity.getParticipantRef().size() > 0) { /* Take first participant and set the flag on its shape element */ Participant p = retrieveParticipantForMessage(choreoActivity, message); if(p != null) { setMessageVisibleForParticipant(p, message); } /* * Remove association edge from bpmnElement list, because the * occurrence in the exported XML is not allowed. */ removeBpmnElement(association); return; } Participant p = null; if (association.getSourceRef() instanceof Participant && association.getTargetRef() instanceof Message) { p = (Participant) association.getSourceRef(); message = (Message) association.getTargetRef(); } else if (association.getTargetRef() instanceof Participant && association.getSourceRef() instanceof Message) { p = (Participant) association.getTargetRef(); message = (Message) association.getSourceRef(); } else { return; } setMessageVisibleForParticipant(p, message); /* * Remove association edge from bpmnElement list, because the occurrence * in the exported XML is not allowed. */ removeBpmnElement(association); return; } /** * * * @param choreoActivity * @return */ private Participant retrieveParticipantForMessage(ChoreographyActivity choreoActivity, Message m) { if(choreoActivity.getParticipantRef().isEmpty()) { return null; } BPMNShape messageShape = (BPMNShape) bpmnElements.get(m.getId()).getShape(); double minDistance = -1.0; Participant returnParticipant = null; for(Participant p : choreoActivity.getParticipantRef()) { BPMNShape participantShape = (BPMNShape) bpmnElements.get(p.getId()).getShape(); double distance = DiagramHelper.calculateCenterDistance(messageShape.getBounds(), participantShape.getBounds()); if(minDistance == -1.0 || distance < minDistance) { minDistance = distance; returnParticipant = p; } } return returnParticipant; } /** * Makes the participant's message visible and stores the name attribute of * a message as a {@link SignavioMessageName} extension element. * * @param p * @param m */ private void setMessageVisibleForParticipant(Participant p, Message m) { BPMNShape pShape = (BPMNShape) bpmnElements.get(p.getId()) .getShape(); pShape.setIsMessageVisible(Boolean.TRUE); /* Store name of message element */ if(m.getName() != null && m.getName().length() > 0) { ExtensionElements extEle = p.getOrCreateExtensionElements(); extEle.add(new SignavioMessageName(m.getName())); } /* Set ref to message for later processing */ p._msgRef = m; } /** * If a process contains choreography elements the process will be inserted * into a choreography element. */ private void insertChoreographyProcessesIntoDefinitions() { for (Process p : this.processes) { if (!p.isChoreographyProcess()) continue; Choreography choreo = new Choreography(); this.getChoreography().add(choreo); choreo.setId(p.getId()); choreo.setName(p.getName()); choreo.setIsClosed(p.isIsClosed()); /* * Insert shapes of the choreograhy elements except edge shape, they * will be appended later to appear on most top layer of the * diagram. */ for (FlowElement flowEle : p.getFlowElement()) { /* Move association into other list */ if (flowEle instanceof Association) { Association association = (Association) flowEle; /* * Check whether the association depicts a message to the * choreography activity */ if ((association.getSourceRef() instanceof ChoreographyActivity && association .getTargetRef() instanceof Message) || (association.getSourceRef() instanceof Message && association .getTargetRef() instanceof ChoreographyActivity) // || (association.getSourceRef() instanceof Participant // && association.getTargetRef() instanceof Message) // || (association.getSourceRef() instanceof Message // && association.getTargetRef() instanceof Participant) ) { handleMessageAssociationOnChoreographyActivity(association); continue; } choreo.getAssociation().add((Association) flowEle); continue; } choreo.getFlowElement().add(flowEle); if (!(flowEle instanceof Edge) && !(flowEle instanceof Message)) { this.definitions.getFirstPlane().getDiagramElement().add( this.bpmnElements.get(flowEle.getId()).getShape()); /* Insert participant band elements */ if (flowEle instanceof ChoreographyActivity) { for (Participant participant : ((ChoreographyActivity) flowEle) .getParticipantRef()) { this.definitions.getFirstPlane() .getDiagramElement().add( this.bpmnElements.get( participant.getId()) .getShape()); } } } /* Insert participants of activities into choreography */ if (flowEle instanceof ChoreographyTask) { ChoreographyTask choreoAct = (ChoreographyTask) flowEle; /* Insert a message flow from first to last participant */ choreoAct.createMessageFlows(choreo); /* Insert the message objects */ insertMessageElements(choreoAct); choreo.getParticipant().addAll( ((ChoreographyActivity) flowEle) .getParticipantRef()); } /* Handle subchoreographies recursively */ if (flowEle instanceof SubChoreography) { choreo.getParticipant().addAll( ((ChoreographyActivity) flowEle) .getParticipantRef()); ((SubChoreography) flowEle).setParticipantsAndMessageFlows( choreo, this.bpmnElements, this); /* Insert child shape element of a sub choreography */ List<String> idList = ((SubChoreography) flowEle) .getIdsOfDiagramElements(); for (String id : idList) { BPMNElement bpmnEl = this.bpmnElements.get(id); if(bpmnEl != null) { this.definitions.getFirstPlane().getDiagramElement() .add(bpmnEl.getShape()); } } } } /* * Remove Message elements, because they are not represented by a * shape element */ for (FlowElement flowEle : p.getFlowElement()) { if (flowEle instanceof Message) { this.bpmnElements.remove(this.bpmnElements.get(flowEle .getId())); Association msgAssociation = ((Message) flowEle) .getDataConnectingAssociation(); if (msgAssociation != null) { this.bpmnElements.remove(this.bpmnElements .get(msgAssociation.getId())); } } } /* Insert Artifact elements */ for (Artifact artifact : p.getArtifact()) { if (artifact.getSubChoreography() != null) continue; /* Semantic element */ choreo.getArtifact().add(artifact); /* DI element */ this.definitions.getFirstPlane().getDiagramElement().add( bpmnElements.get(artifact.getId()).getShape()); } /* * Set bpmn plane reference, maybe overwritten by another * collaboration later */ this.definitions.getFirstPlane().setBpmnElement(choreo); } if (this.choreography != null) { /* Insert into definitions */ this.definitions.getRootElement().addAll(this.choreography); } } /** * Inserts the {@link Message} object related to that choreography task * @param ct */ private void insertMessageElements(ChoreographyTask ct) { for(MessageFlow mf : ct.getMessageFlows()) { if(mf.getMessageRef() != null) { this.definitions.getRootElement().add(mf.getMessageRef()); } } } /** * Sets attributes of the {@link Definitions} element. */ private void setDefinitionsAttributes() { /* Set targetnamespace */ String targetnamespace = diagram.getProperty("targetnamespace"); if (targetnamespace == null) targetnamespace = "http://www.signavio.com/bpmn20"; this.definitions.setTargetNamespace(targetnamespace); /* Set Name attribute */ String name = diagram.getProperty("name"); if(name != null && name.length() > 0) { this.definitions.setName(name); } /* Export Tool Information */ this.definitions .setExporter("Signavio Process Editor, http://www.signavio.com"); this.definitions .setExporterVersion((editorVersion != null ? editorVersion : "")); /* Additional namespace definitions */ try { String namespacesProperty = this.diagram.getProperty("namespaces"); JSONObject namespaces = new JSONObject(namespacesProperty); JSONArray namespaceItems = namespaces.getJSONArray("items"); /* * Retrieve namespace declarations and put them to namespaces * attribute. */ for (int i = 0; i < namespaceItems.length(); i++) { JSONObject namespace = namespaceItems.getJSONObject(i); this.definitions.getNamespaces().put( namespace.getString("prefix"), namespace.getString("url")); } } catch (JSONException e) { // ignore namespace property } catch (NullPointerException np) { } /* Expression Language */ String exprLanguage = diagram.getProperty("expressionlanguage"); if (exprLanguage != null && !(exprLanguage.length() == 0)) this.definitions.setExpressionLanguage(exprLanguage); /* Type Language */ String typeLanguage = diagram.getProperty("typelanguage"); if (typeLanguage != null && !(typeLanguage.length() == 0)) this.definitions.setTypeLanguage(typeLanguage); /* Other attributes */ String otherAttrStr = diagram.getProperty("otherattributes"); if(otherAttrStr == null || otherAttrStr.length() == 0) { return; } // process as json array containing json objects try { JSONArray a = new JSONArray(otherAttrStr); for(int i = 0; i < a.length(); i++) { JSONObject o = a.getJSONObject(i); String localpart = o.optString("localpart"); String ns = o.optString("ns"); String prefix = o.optString("prefix"); String value = o.optString("value"); if((localpart != null || ns != null || prefix != null) && value != null) { this.definitions.getOtherAttributes().put(new QName((ns != null ? ns : ""), (localpart != null ? localpart : ""), (prefix != null ? prefix : "")), value); } } } catch (JSONException e) { } } /** * Method to create input output specification based on data inputs and * outputs. */ private void setIOSpecification() { for (Task t : this.getAllTasks()) t.determineIoSpecification(); } /** * Retrieves all elements like Signals and puts them in the appropriate * field of the {@link Definitions} element. */ private void putGlobalElementsIntoDefinitions() { for (BPMNElement element : this.bpmnElements.values()) { /* Signals */ if (element.getNode() instanceof Event && ((Event) element.getNode()).isSignalEvent()) { SignalEventDefinition sigEvDev = (SignalEventDefinition) ((Event) element .getNode()) .getEventDefinitionOfType(SignalEventDefinition.class); sigEvDev.insertSignalIntoDefinitions(definitions); } } /* Messages */ this.retrieveAndInsertMessages(); } /** * Inserts all edge diagram element of the BPMN diagram. */ private void insertEdgeDiagramElements() { Process unmappedElementsProcess = new Process(); unmappedElementsProcess.setId(SignavioUUID.generate()); for (BPMNElement element : this.bpmnElements.values()) { if (element.getNode() instanceof Edge) { /* Associations */ if (element.getNode() instanceof Association && ((Association) element.getNode())._containedInProcess) { definitions.getFirstPlane().getDiagramElement().add( element.getShape()); } /* Data Associations */ if (element.getNode() instanceof DataAssociation) { DataAssociation dataAsso = (DataAssociation) element.getNode(); if(dataAsso.getSourceRef() != null && dataAsso.getTargetRef() != null && (dataAsso.getSourceRef() instanceof Activity || dataAsso.getTargetRef() instanceof Activity)) { definitions.getFirstPlane().getDiagramElement().add( element.getShape()); } } /* Sequence flows */ if (element.getNode() instanceof SequenceFlow) { /* * Check if the sequence flow element is assigned to a * process element. */ if (((SequenceFlow) element.getNode()).getProcess() == null && ((SequenceFlow) element.getNode()) .getSubChoreography() == null && ((SequenceFlow) element.getNode()) .getSubProcess() == null) { unmappedElementsProcess.getFlowElement().add( (FlowElement) element.getNode()); } definitions.getFirstPlane().getDiagramElement().add( element.getShape()); } /* Message flows */ if (element.getNode() instanceof MessageFlow) { definitions.getFirstPlane().getDiagramElement().add( element.getShape()); } /* Conversation Links */ if (element.getNode() instanceof ConversationLink) { definitions.getFirstPlane().getDiagramElement().add( element.getShape()); } } } if (unmappedElementsProcess.getFlowElement().size() > 0) { this.definitions.getRootElement().add(unmappedElementsProcess); } } /** * Retrieves a BPMN 2.0 diagram and transforms it into the BPMN 2.0 model. * * @param diagram * The BPMN 2.0 {@link GenericDiagram} based on the ORYX JSON. * @return The definitions root element of the BPMN 2.0 model. * @throws BpmnConverterException */ public Definitions getDefinitionsFromDiagram() throws BpmnConverterException { /* Build-up the definitions as root element of the document */ this.setDefinitionsAttributes(); /* Convert shapes to BPMN 2.0 elements */ try { createBpmnElementsRecursively(diagram); } catch (Exception e) { /* Pack exceptions in a BPMN converter exception */ throw new BpmnConverterException( "Error while converting to BPMN model", e); } this.detectBoundaryEvents(); this.detectConnectors(); this.setInitiatingParticipant(); /* Section to handle data concerning aspects */ this.updateDataAssociationsRefs(); this.setIOSpecification(); this.setDefaultSequenceFlowOfExclusiveGateway(); this.setCompensationEventActivityRef(); // this.setConversationParticipants(); this.identifyProcesses(); this.handleDataObjects(); this.handleArtifacts(); this.addAssociationsToProcess(); this.addAssociationsToConversation(); this.createProcessIOSpec(); this.processChoreographies(); /* Globally defined elements */ this.putGlobalElementsIntoDefinitions(); this.putCalledElementsIntoDefinitions(); /* Insert elements into diagram */ this.insertCollaborationElements(); this.insertChoreographyProcessesIntoDefinitions(); this.insertProcessesIntoDefinitions(); /* * Insert diagram element of edge last, because they are on top of all * other elements */ this.insertEdgeDiagramElements(); this.insertDiagramsForLinkedSubprocesses(); this.determineUnusedNamespaceDeclarations(); this.collectExternalNamespaces(); this.ensureSignavioUUIDStyle(); this.checkUniquenessOfProcessIDs(); return definitions; } /** * Encapsulates methods for process task related to the export of * choreography elements. */ private void processChoreographies() { for (Process p : this.processes) { if (!p.isChoreographyProcess()) continue; for (FlowElement flowEle : p.getFlowElement()) { /* Handle messages connected to a choreography task */ if (flowEle instanceof Association) { Association association = (Association) flowEle; /* * Check whether the association depicts a message to the * choreography activity */ if ((association.getSourceRef() instanceof ChoreographyActivity && association .getTargetRef() instanceof Message) || (association.getSourceRef() instanceof Message && association .getTargetRef() instanceof ChoreographyActivity) ) { handleMessageAssociationOnChoreographyActivity(association); } } } } } /** * Ensures all element id have the same Signavio defined style */ private void ensureSignavioUUIDStyle() { // if(this.configuration != null && this.configuration.ensureSignavioStyle) { for(BPMNElement bpmnEl : bpmnElements.values()) { if(!SignavioIDChecker.isValidID(bpmnEl.getId(), this.configuration)) { /* * Generate and set new ID */ String newId = SignavioUUID.generate(); bpmnEl.setId(newId); bpmnEl.getNode().setId(newId); bpmnEl.getShape().setId(newId + "_gui"); } } // } } /** * Retrieves the called elements and puts them into the root elements section * as global defined elements. */ private void putCalledElementsIntoDefinitions() { for(BPMNElement bpmnEl : bpmnElements.values()) { if(bpmnEl.getNode() instanceof CallingElement) { this.definitions.getRootElement().addAll(((CallingElement) bpmnEl.getNode()).getCalledElements()); } } } /** * Creates the {@link InputOutputSpecification} for each process element. * Every data input and output element is moved to the {@link InputOutputSpecification} * and an additional data input/output reference is created. */ private void createProcessIOSpec() { for(Process p : this.processes) { InputOutputSpecification ioSpec = new InputOutputSpecification(); OutputSet outputSet = new OutputSet(); InputSet inputSet = new InputSet(); ioSpec.getOutputSet().add(outputSet); ioSpec.getInputSet().add(inputSet); List<FlowElement> elementsToBeRemoved = new ArrayList<FlowElement>(); for(FlowElement flowEl : p.getFlowElement()) { if (flowEl instanceof DataInput) { ioSpec.getDataInput().add((DataInput) flowEl); inputSet.getDataInputRefs().add((DataInput) flowEl); elementsToBeRemoved.add(flowEl); } else if (flowEl instanceof DataOutput) { ioSpec.getDataOutput().add((DataOutput) flowEl); outputSet.getDataOutputRefs().add((DataOutput) flowEl); elementsToBeRemoved.add(flowEl); } } p.getFlowElement().removeAll(elementsToBeRemoved); /* Insert specification */ if(ioSpec.getDataInput().size() > 0 || ioSpec.getDataOutput().size() > 0) { p.setIoSpecification(ioSpec); } } } /** * Collects all {@link Message}s referenced by {@link MessageFlow}s and * inserts them as global defined elements. */ private void retrieveAndInsertMessages() { for (BPMNElement bpmnElement : bpmnElements.values()) { /* Message attached to a message flow */ if(bpmnElement.getNode() instanceof MessageFlow) { MessageFlow msgFlow = (MessageFlow) bpmnElement.getNode(); if(msgFlow.getMessageRef() != null) { this.definitions.getRootElement().add(msgFlow.getMessageRef()); } } /* Ignore messages on the canvas */ else if(bpmnElement.getNode() instanceof Message) { Message m = (Message) bpmnElement.getNode(); if(m.getProcess() != null && !m.getProcess().isChoreographyProcess()) { m.getProcess().removeChild(m); // this.definitions.getRootElement().add(m); // this.definitions.getFirstPlane().getDiagramElement().add(this.bpmnElements.get(m.getId()).getShape()); } } } } private void insertDiagramsForLinkedSubprocesses() { for (BPMNElement bpmnel : this.bpmnElements.values()) { /* * Basic subprocesses, sub and call choreographies */ if (bpmnel.getNode() instanceof ContainerElement) { List<BPMNDiagram> diagramList = createBPMNDiagramForContainerElement((ContainerElement) bpmnel.getNode()); this.definitions.getDiagram().addAll(diagramList); } /* * Insert choreography element referenced by call choreography */ if(bpmnel.getNode() instanceof CallChoreography) { CallChoreography callChoreo = (CallChoreography) bpmnel.getNode(); if(callChoreo.getCalledChoreographyRef() != null && !(callChoreo.getCalledChoreographyRef() instanceof GlobalChoreographyTask)) { this.definitions.getRootElement().add(callChoreo.getCalledChoreographyRef()); } } /* * Insert elements referenced by a collapsed call activity sub process */ if(bpmnel.getNode() instanceof CallActivity && ((CallActivity) bpmnel.getNode())._diagramElement != null) { this.definitions.getDiagram().add(((CallActivity) bpmnel.getNode())._diagramElement); } } } /** * Creates the {@link BPMNDiagram}s for a collapsed sub process and * choreography . * * @param container * * @return */ private List<BPMNDiagram> createBPMNDiagramForContainerElement( ContainerElement container) { List<BPMNDiagram> diagramList = new ArrayList<BPMNDiagram>(); if (container._getDiagramElements().size() != 0) { BPMNDiagram diagram = new BPMNDiagram(); diagram.getBPMNPlane().setBpmnElement((BaseElement) container); diagram.getBPMNPlane().getDiagramElement().addAll( container._getDiagramElements()); diagramList.add(diagram); } /* Child subprocesses */ for (FlowElement flowEl : container.getFlowElement()) { if (flowEl instanceof ContainerElement) { List<BPMNDiagram> subDiagramList = createBPMNDiagramForContainerElement((ContainerElement) flowEl); diagramList.addAll(subDiagramList); } } return diagramList; } /* Getter & Setter */ /** * @return The list of BPMN 2.0 's stencil set edgeIds */ public static HashSet<String> getEdgeIds() { return edgeIds; } /** * @return the collaboration */ private Collaboration getCollaboration() { if (this.collaboration == null) { this.collaboration = new Collaboration(); this.collaboration.setId(SignavioUUID.generate()); } return this.collaboration; } /** * @return the choreography element. If an collaboration already exists it * it converts the collaboration to an choreography element. */ private List<Choreography> getChoreography() { // /* Neither collaboration nor choreography existing */ // if (this.collaboration == null) { // this.collaboration = new Choreography(); // } // /* Collaboration exiting */ // else if(this.collaboration instanceof Collaboration) // return (Choreography) this.collaboration; if (this.choreography == null) { this.choreography = new ArrayList<Choreography>(); } return this.choreography; } /** * Removes the {@link BPMNElement} identified by the contained * {@link BaseElement} from the list of all BPMNElements. * * @param node */ private void removeBpmnElement(BaseElement node) { BPMNElement bpmnElement = null; for (BPMNElement element : bpmnElements.values()) { if (element.getNode().equals(node)) { bpmnElement = element; break; } } if (bpmnElement != null) { bpmnElements.remove(bpmnElement.getId()); } } /** * Checks if a vendor specific namespace declaration is not in use and marks * it for removal. */ private void determineUnusedNamespaceDeclarations() { List<String> unusedNamespacePrefixes = new ArrayList<String>( BPMNPrefixMapper.getCustomExtensions().values()); for (BPMNElement element : this.bpmnElements.values()) { if (element.getCustomNamespaces() != null) { for (String prefix : Arrays.asList(element .getCustomNamespaces())) { unusedNamespacePrefixes.remove(prefix); } } } this.definitions.unusedNamespaceDeclarations = unusedNamespacePrefixes; } /** * Collects the namespace definitions provided by xml elements from * external sources like Activiti */ private void collectExternalNamespaces() { Map<String, String> nsDefs = new HashMap<String, String>(); for(BPMNElement el : this.bpmnElements.values()) { nsDefs.putAll(el.getExternalNamespaceDefinitions()); nsDefs.putAll(el.getNode().getExternalNamespaceDefinitions()); } this.definitions.externalNSDefs = nsDefs; } /** * Checks if all process IDs are valid and generates new ones in case of * a negative check. */ private void checkUniquenessOfProcessIDs() { Set<String> pIDs = new HashSet<String>(); for(Process p : this.processes) { if(!p.hasId() || pIDs.contains(p.getId())) { p.setId(SignavioUUID.generate()); pIDs.add(p.getId()); } pIDs.add(p.getId()); } } public static Constants getConstants() { return constants; } public static void setConstants(Constants constants) { Diagram2BpmnConverter.constants = constants; } }